/*!*
*    Provides event handling for iPhone like photo gallery (without thumbnail portion)
*
*    @author Sam Shull <http://www.google.com/profiles/brickysam26>
*    @copyright 2010 Sam Shull <http://samshull.blogspot.com/>
*
*    Special Thanks to Steve Simitzis <http://saturn5.com>
*
*    @license <http://www.opensource.org/licenses/mit-license.html>
*
*    Permission is hereby granted, free of charge, to any person obtaining a copy
*    of this software and associated documentation files (the "Software"), to deal
*    in the Software without restriction, including without limitation the rights
*    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*    copies of the Software, and to permit persons to whom the Software is
*    furnished to do so, subject to the following conditions:
*    
*    The above copyright notice and this permission notice shall be included in
*    all copies or substantial portions of the Software.
*    
*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*    THE SOFTWARE.
*
*    Custom Events handled by each gallery:
*
*        jqt-photo-slideto            - slide to a given index (e, [index = int [, options = Object [, slides = jQuery ] ] ] )
*        jqt-photo-goto            - jump to a given index (e, [index = int [, options = Object [, slides = jQuery ] ] ] )
*        jqt-photo-play                - start the slideshow from the given point
*        jqt-photo-pause                - stop the slideshow
*        jqt-photo-prev                - go to the previous slide
*        jqt-photo-next                - go to the next slide
*        jqt-photo-hide-toolbars        - hide the visibility of the toolbars
*        jqt-photo-show-toolbars        - show the visibility of the toolbars
*        jqt-photo-toggle-toolbars    - toggle the visibility of the toolbars
*/
(function($){
	//we need this in the algorithm
	if (!"WebKitCSSMatrix" in this) {
		return null;
	}
	
	/**
	*
	*
	*
	*/
	var undefined, 
		
		/**
		*    keep track of the registered galleries for updates on resize
		*
		*    @var Array<Element>
		*/
		galleries = [],
		
		/**
		*    Does the browser support Touch
		*
		*    @var Boolean
		*/
		supportsTouch = "Touch" in this,
		
		/**
		*    event types
		*
		*    @var Object
		*/
		events = supportsTouch ? {
			start: "touchstart",
			move: "touchmove",
			end: "touchend"
		} : {
			start: "mousedown",
			move: "mousemove",
			end: "mouseup"
		},
		
		controlPoints,
		
		/**
		*    keep track of whether or not the parsed class rule has been inserted
		*
		*    @var Boolean
		*/
		parseRuleSet = false,
		
		//bring into context
		
		/**
		*    
		*
		*    @var Window
		*/
		window = this,
		
		/**
		*    
		*
		*    @var Document
		*/
		document = window.document,
		
		/**
		*    
		*
		*    @var Object
		*/
		Math = window.Math,
		
		//simplify access to these functions
		
		/**
		*    
		*
		*    @var Function
		*/
		min = Math.min,
		
		/**
		*    
		*
		*    @var Function
		*/
		floor = Math.floor,
		
		/**
		*    
		*
		*    @var Function
		*/
		sqrt = Math.sqrt,
		
		/**
		*    
		*
		*    @var Function
		*/
		pow = Math.pow,
		
		/**
		*    
		*
		*    @var Function
		*/
		abs = Math.abs,
		
		/**
		*    current orientation
		*
		*    @var String
		*/
		orientation = abs(window.orientation) == 90 ? "landscape" : "portrait",
		
		/**
		*    The default options
		*
		*    @var Object
		*/
		defaults = {
			/**
			*    A list of the images to be displayed in the gallery
			*
			*    @var Array<Object>
			*/
			data: [],
			
			/**
			*    displays in title bar 
			*    - supports format 
			*        {0} = current index
			*        {1} = total number of slides
			*
			*    @var String
			*/
			galleryName: "{0} of {1}",
			
			/**
			*    where to start the slides in the gallery - reset on pageAnimation out
			*
			*    @var Number
			*/
			defaultIndex: 0,
			
			/**
			*    properties to animate
			*
			*    @var Number
			*/
			transitionProperty: "-webkit-transform",
			
			/**
			*    timing function
			*
			*    @var String
			*/
			timingFunction: "cubic-bezier(0,0,.25,1)",
			
			/**
			*    the reset transform of the table
			*
			*    @var String
			*/
			transform: "translate3d({0}px,0,0)",
			
			/**
			*    The template to be used for transforming the IMG tag
			*
			*    @var String
			*/
			imageTransform: "scale({0}) translate3d({1}px, {2}px, 0)",
			
			/**
			*    Maximum number of slides to keep in DOM at on time - before current slide
			*
			*    @var Number
			*/
			maxSlidesBefore: 2,
			
			/**
			*    Maximum number of slides to keep in DOM at on time - after current slide
			*
			*    @var Number
			*/
			maxSlidesAfter: 2,
			
			/**
			*    default delay between transitions for slideshow
			*
			*    @var Number
			*/
			slideDelay: 5000,
			
			/**
			*    default speed for slide change
			*
			*    @var Number
			*/
			scrollSpeed: 500,
			
			/**
			*    default speed for scale resizing - used on scaleEnd
			*
			*    @var Number
			*/
			scaleSpeed: 500,
			
			/**
			*    should we try to dynamically render the CSS rules
			*
			*    @var Boolean
			*/
			useDynamicStyleSheet: true,
			
			/**
			*    should a slideshow repeat by default
			*
			*    @var Boolean
			*/
			repeatSlideShow: false,
			
			/**
			*    selector for the parent of the gallery
			*
			*    @var String
			*/
			appendTo: "#jqt",
			
			/**
			*    the attribute name for storing the index of the slide in the data
			*
			*    @var String
			*/
			dataAttribute: "data-index",
			
			/**
			*    tags to ignore
			*
			*    @var Array
			*/
			tagNames: "A,INPUT,SELECT,TEXTAREA",
			
			//only used by template generator
			
			/**
			*    link to return
			*
			*    @var String
			*/
			backLink: '<a class="back">Back</a>',
			
			/**
			*    a blank image tag's template
			*
			*    @var String
			*/
			blankImage: '<img class="jqt-photo-img"/>',
			
			/**
			*    an individual slides template
			*
			*    @var String
			*/
			slideTemplate: '<td class="jqt-photo-image-slide">\
								<div class="jqt-photo-not-loaded">\
									<img class="jqt-photo-img"/>\
									<div class="jqt-photo-caption"></div>\
								</div>\
							</td>',
			
			/**
			*    CSS Rule for removing the max-width/max-height properties from the images
			*
			*    @var String
			*/
			parsedClassTemplate: "#jqt .jqt-photo .jqt-photo-image-slide > .{0}{max-width:auto;max-height:auto;}",
			
			/**
			*    the overall psuedo page template of a gallery
			*
			*    @var String
			*/
			galleryTemplate: '<div class="jqt-photo">\
									<div class="toolbar toolbar-top"><\/div>\
									<table class="jqt-photo-table">\
										<tr class="jqt-photo-slide-list"><\/tr>\
									<\/table>\
									<div class="toolbar toolbar-bottom">\
										<div class="jqt-photo-prev"></div>\
										<div class="jqt-photo-pause"></div>\
										<div class="jqt-photo-play"></div>\
										<div class="jqt-photo-next"></div>\
									<\/div>\
								<\/div>',
			
			//psuedo events
			
			/**
			*    Handle the pageAnimationEnd event fired by jQTouch
			*    Resets the positions and scales
			*
			*    @var Function
			*/
			pageAnimationEnd: pageAnimationEnd,
			
			/**
			*    Handle the pageAnimationStart event fired by jQTouch
			*    Resets the positions and scales
			*
			*    @var Function
			*/
			pageAnimationStart: pageAnimationStart,
			
			/**
			*    Handle the transition of slides in a gallery
			*
			*    @var Function
			*/
			slideTo: slideTo,
			
			/**
			*    Begin a slideshow on a gallery
			*
			*    @var Function
			*/
			play: play,
			
			/**
			*    End a slideshow on a gallery
			*
			*    @var Function
			*/
			pause: pause,
			
			/**
			*    Go to the next slide in the list
			*
			*    @var Function
			*/
			next: next,
			
			/**
			*    Go to the previous slide in the list
			*
			*    @var Function
			*/
			prev: prev,
			
			/**
			*    Go to a specific or implied slide
			*
			*    @var Function
			*/
			goTo: goTo,
			
			/**
			*    Reveal the toolbars
			*
			*    @var Function
			*/
			showToolbars: showToolbars,
			
			/**
			*    Hide the toolbars
			*
			*    @var Function
			*/
			hideToolbars: hideToolbars,
			
			/**
			*    Show/Hide the toolbars where appropriate
			*
			*    @var Function
			*/
			toggleToolbars: toggleToolbars,
			
			//mouse events
			
			/**
			*    Handle a touchstart event on the gallery
			*
			*    @var Function
			*/
			touchStart: touchStart,
			
			/**
			*    Handle a drag start event (triggered by touchstart) - one-finger
			*
			*    @var Function
			*/
			dragStart: dragStart,
			
			/**
			*    Event handler - move - one-finger
			*
			*    @var Function
			*/
			drag: drag,
			
			/**
			*    Event handler - end - one-finger
			*
			*    @var Function
			*/
			dragEnd: dragEnd,
			
			/**
			*    Event handler - start - two-fingers
			*
			*    @var Function
			*/
			scaleStart: scaleStart,
			
			/**
			*    Event handler - move - two-fingers
			*
			*    @var Function
			*/
			scale: scale,
			
			/**
			*    Event handler - end - two-fingers
			*
			*    @var Function
			*/
			scaleEnd: scaleEnd,
			
			/**
			*    An event handler for the IMG load event
			*
			*    @var Function
			*/
			loader: loader,
			
			/**
			*    Function provided for generating the slides
			*
			*    @var Function
			*/
			createSlide: createSlide,
			
			/**
			*    Function provided for maintaining the sliding window of slides
			*
			*    @var Function
			*/
			rearrange: rearrange,
			
			//classes
			
			/**
			*    Keep track of preloaded images
			*
			*    @var String
			*/
			presizedClass: "jqt-photo-presized",
			
			/**
			*    keep track of images that have data
			*
			*    @var String
			*/
			parsedClass: "jqt-photo-parsed",
			
			/**
			*    Class name for identifying the current slide
			*
			*    @var String
			*/
			currentClass: "jqt-photo-current",
			
			/**
			*    Class name for designating that a slideshow is in progress 
			*    (and cause pause icon to show, not play icon)
			*
			*    @var String
			*/
			playingClass: "jqt-photo-playing",
			
			/**
			*    Class name for designating that an image is not currently loaded
			*
			*    @var String
			*/
			notLoadedClass: "jqt-photo-not-loaded",
			
			/**
			*    Class name for initiating an animation to show the toolbars
			*
			*    @var String
			*/
			toolbarAnimationInClass: "jqt-photo-toolbar-animation-in",
			
			/**
			*    Class name for initiating an animation to hide the toolbars
			*
			*    @var String
			*/
			toolbarAnimationOutClass: "jqt-photo-toolbar-animation-out",
			
			/**
			*    Class name for hiding toolbars - applied outside of animation
			*
			*    @var String
			*/
			toolbarHideClass: "jqt-photo-toolbar-hidden",
			
			//selectors
			
			/**
			*    The selector for the container of the slides
			*
			*    @var String
			*/
			tableSelector: ".jqt-photo-table",
			
			/**
			*    The selector for the caption of a slide
			*
			*    @var String
			*/
			captionSelector: ".jqt-photo-caption",
			
			/**
			*    The selector of a slide container
			*
			*    @var String
			*/
			slideSelector: ".jqt-photo-image-slide",
			
			/**
			*    The selector of the scalable element, relative to a slide
			*
			*    @var String
			*/
			imageSelector: "img.jqt-photo-img",
			
			/**
			*    The selector of the title element
			*
			*    @var String
			*/
			titleSelector: ".toolbar-top h1",
			
			/**
			*    The selector of row that contains the images
			*
			*    @var String
			*/
			listSelector: ".jqt-photo-slide-list",
			
			/**
			*    The selector of the Play Icon
			*
			*    @var String
			*/
			playSelector: ".jqt-photo-play",
			
			/**
			*    The selector of the Pause Icon
			*
			*    @var String
			*/
			pauseSelector: ".jqt-photo-pause",
			
			/**
			*    The selector of the Next Icon
			*
			*    @var String
			*/
			nextSelector: ".jqt-photo-next",
			
			/**
			*    The selector of the Previous Icon
			*
			*    @var String
			*/
			prevSelector: ".jqt-photo-prev",
			
			/**
			*    The selector of a gallery element
			*
			*    @var String
			*/
			gallerySelector: ".jqt-photo"
		},
		
		/**
		*    Calculate the desired position of the caption elements within the galleries
		*
		*    @var Function
		*/
		caption = function(vars){
			if (abs(window.orientation) == 90) {
				return (270 - vars.caption) + "px";
			} else {
				return (420 - vars.caption) + "px";
			}
			// return (window.innerHeight - vars.caption) + "px";
		},
		
		/**
		*    Calculate the desired height of the galleries and the slides
		*
		*    @var Function
		*/
		height = function() {
			if (abs(window.orientation) == 90) {
				return "270px";
			} else {
				return "420px";
			}
			// return window.innerHeight + "px";
		},
		
		/**
		*    Calculate the desired width of the galleries and the slides
		*
		*    @var Function
		*/
		width = function() {
			if (abs(window.orientation) == 90) {
				return "480px";
			} else {
				return "320px";
			}
			// return window.innerWidth + "px";
		},
		
		/**
		*    Calculate the desired position of the bottom toolbar elements within the galleries
		*
		*    @var Function
		*/
		toolbar = function(vars){
			if (abs(window.orientation) == 90) {
				return (270 - vars.toolbar) + "px";
			} else {
				return (420 - vars.toolbar) + "px";
			}
			// return (window.innerHeight - vars.toolbar) + "px";
		},
		
		/**
		*    The default CSS rules for dynamic insertion
		*
		*    @var Object
		*/
		cssRules = {
		
			/**
			*    Variables used by the CSS "macros" for dynamically calculating positions and dimensions
			*
			*    @var Object
			*/
			variables : {
				caption: 90,
				toolbar: 45
			},
		
			/**
			*    Default CSS rules
			*
			*    @var Object
			*/
			defaults: {
				"#jqt .jqt-photo" : {
					width: width,
					height: height,
				},
				"#jqt .jqt-photo .toolbar-bottom" : {
					top: toolbar
				},
				"#jqt .jqt-photo .jqt-photo-image-slide > div" : {
					width: width,
					height: height,
					"line-height": height
				},
				"#jqt .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
					top: caption,
				}
			},
		
			/**
			*    Portrait view CSS rules
			*
			*    @var Object
			*/
			portrait: {
				"#jqt.portrait .jqt-photo" : {
					height: height,
					width: width,
				},
				"#jqt.portrait .jqt-photo .toolbar-bottom" : {
					top: toolbar
				},
				"#jqt.portrait .jqt-photo .jqt-photo-image-slide > div" : {
					width: width,
					height: height,
					"line-height": height,
				},
				"#jqt .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
					top: caption,
				}
			},
		
			/**
			*    Landscape view CSS rules
			*
			*    @var Object
			*/
			landscape: {
				"#jqt.landscape .jqt-photo" : {
					height: height,
					width: width,
				},
				"#jqt.landscape .jqt-photo .toolbar-bottom" : {
					top: toolbar,
				},
				"#jqt.landscape .jqt-photo .jqt-photo-table" : {
					height: height,
				},
				"#jqt.landscape .jqt-photo .jqt-photo-image-slide > div" : {
					height: height,
					width: width,
					"line-height": height,
				},
				"#jqt.landscape .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
					top: caption,
				}
			}
		};
	
	/*
	*    jQTouch extension
	*/
	if ($.jQTouch) {
		//bind the extension
		$.jQTouch.addExtension(function(jqt) {   
			/*/event handler
			function binder (event, info) {
				if (info.page.is(defaults.gallerySelector)) {
					info.page.jqtPhoto();
				}
			}
			
			//attach
			$(document.body).bind("pageInserted", binder);
			
			//attach to appropriate containers on DOMREADY
			$(function() {
				$(defaults.gallerySelector)
					.each(function() {
						binder(null, {page: $(this)});
					});
			});
			*/
			return {
				generateGallery: generateGallery, 
				/**
				*    Jump to the index of a slide in a gallery
				*
				*    @param jQuery | String (selector) | Element gallery
				*    @param Number index
				*    @param String | Object animation
				*    @param Boolean reverse
				*    @return jQuery
				*/
				goToSlide: function (gallery, index, animation, reverse) {
					//trigger this first
					var g = $(gallery), options = g.data("jqt-photo-options"),slides = g.find(options.slideSelector);
					
					if (slides.index("."+options.currentClass) != index) {
						slides.removeClass(options.currentClass).eq(index).addClass(options.currentClass);
					}
					
					if (!g.hasClass("current")) {
						jqt.goTo(g, animation || "slide", reverse);
					}
					
					g.triggerHandler("jqt-photo-show-toolbars");
					
					return g;
				}
			};
		});
	}
	
	/**
	*    Generate a gallery and attach it to the #jqt element,
	*    additionally updates scaling data, so you don't have to
	*
	*    <code>
	*        images = [{src:"/somewhere1.jpg",caption:"Not Yet Implemented",width:200,height:200},
	*                  {src:"/somewhere2.jpg",caption:"Not Yet Implemented"}];
	*    </code>
	*
	*    @param String id
	*    @param Array<Object> images
	*    @param Object options - @see defaults
	*    @return jQuery
	*/
	function generateGallery (id, images, options) {
		options = $.extend({}, defaults, options);
		
		options.data = images;
		
		if (!parseRuleSet) {
			parseRuleSet = true;
			var sheet = document.styleSheets[document.styleSheets.length-1];
			sheet.insertRule(
				format(options.parsedClassTemplate, options.parsedClass), 
				sheet.cssRules.length
			);
		}
		
		options.gallery = $(options.galleryTemplate).appendTo(options.appendTo);
		
		var list = options.list = options.gallery.find(options.listSelector),
			lower = options.defaultIndex - options.maxSlidesBefore,
			upper = options.defaultIndex + options.maxSlidesAfter,
			toolbar = options.gallery.attr("id", id)
						.find(".toolbar-top")
						.append(
							$("<h1></h1>").html(
								format(options.galleryName, options.defaultIndex + 1, images.length)
							)
						);
		
		if (options.backLink) {
			toolbar.append(options.backLink);
		}
		
		$.each(images, function(i, data) {
			list.append(options.createSlide(data, i, options, i >= lower && i <= upper));
		});
		
		return jqtPhoto(options.gallery[0], options);
	}
	
	/*
	*
	*    jQuery Extensions
	*
	*/
	
	/**
	*    A jQuery prototype extension for enabling the photo gallery
	*    on a given set of elements
	*
	*    @param Object options
	*    @return jQuery
	*/
	$.fn.jqtPhoto = function(options) {
		options = $.extend({}, defaults, options || {});
		return this.each(function(){jqtPhoto(this, options)});
	};
	
	/**
	*    A static jQuery extension for setting and retrieving the jQT-Photo defaults
	*
	*    
	*/
	$.jqtPhoto = {
		generateGallery: generateGallery,
	
		/**
		*    Change and/or retrieve the default options
		*
		*    @param Object options - optional
		*    @return Object
		*/
		defaults: function(options) {
			if (options) {
				defaults = $.extend(defaults, options);
			}
			
			return $.extend({}, defaults);
		},
	
		/**
		*    Change and/or retrieve the default CSS
		*
		*    @param Object options - optional
		*    @return Object
		*/
		defaultCSS: function(options) {
			if (options !== undefined) {
				cssRules = $.extend(true, cssRules, options);
			}
			
			return $.extend({}, cssRules);
		}
	};
	
	/*
	*
	*    Functions
	*
	*/
	
	/**
	*    Intialize the photo gallery
	*
	*    @param Element | String | jQuery element
	*    @param Object options
	*    @return jQuery
	*/
	function jqtPhoto (element, options) {
		var $elem = $(element),
			slides = attachEvents($elem, options).find(options.slideSelector),
			images = slides.find(options.imageSelector);
		
		options.list = $elem.find(options.listSelector);
		
		options.table = tableData($elem.data("jqt-photo-options", options), options);
		
		options.blankImage = parseImageData($elem, $(options.blankImage).load(options.loader), options);
		
		if (!slides.filter("."+options.currentClass).length) {
			if (!slides.filter(format("[{0}={1}]", options.dataAttribute, options.defaultIndex)).addClass(options.currentClass).length) {
				slides.eq(0).addClass(options.currentClass);
			}
		}
		
		$elem.find(options.tableSelector).css({
			webkitTransitionProperty: options.transitionProperty,
			webkitTransitionTimingFunction: options.timingFunction,
			webkitTransitionDuration: options.defaultDuration + "s",
			webkitTransform: format(options.transform, -slides.filter("."+options.currentClass).attr("offsetLeft") || 0)
		});
		
		parseImageData($elem, images, options);
		
		galleries = galleries.concat($.makeArray($elem));
		
		return $elem;
	}
	
	/**
	*    Record the initial start position of the image list element
	*
	*    @param jQuery target
	*    @param Object options
	*    @return null
	*/
	function tableData (target, options) {
		var table = target.find(options.tableSelector),
			transform = window.getComputedStyle(table[0]).webkitTransform,
			matrix = new WebKitCSSMatrix(transform);
		
		return table.data("jqt-photo-position", {x: Number(matrix.m41 || 0), gallery: target});
	}
	
	/**
	*    Attach event listeners to the gallery
	*
	*    To Do: refractor event listeners into delegate
	*
	*    @param jQuery target
	*    @param Object options
	*    @return jQuery
	*/
	function attachEvents (target, options) {
		
		return target
				.each(function(){this.addEventListener(events.start, options.touchStart, false)})
				.bind("jqt-photo-slideto", options.slideTo)
				.bind("jqt-photo-goto", options.goTo)
				.bind("jqt-photo-play", options.play)
				.bind("jqt-photo-pause", options.pause)
				.bind("jqt-photo-prev", options.prev)
				.bind("jqt-photo-next", options.next)
				.bind("jqt-photo-hide-toolbars", options.hideToolbars)
				.bind("jqt-photo-show-toolbars", options.showToolbars)
				.bind("jqt-photo-toggle-toolbars", options.toggleToolbars)
				.bind("pageAnimationEnd", options.pageAnimationEnd)
				.bind("pageAnimationStart", options.pageAnimationStart);
	}
	
	/**
	*    Record initial slide data
	*
	*    @param jQuery images
	*    @param Object options
	*    @return jQuery
	*/
	function parseImageData (target, images, options) {
		return images.each(function() {
			var t = $(this).addClass(options.parsedClass),
				matrix = new WebKitCSSMatrix(window.getComputedStyle(this).webkitTransform),
				scale = Number(matrix.m11),
				data = t.data("jqt-photo-info") || {}, 
				c = {
					scale: scale,
					top: Number(matrix.m42),
					left: Number(matrix.m41),
					width: t.width() * scale,
					height: t.height() * scale,
					parent: t.parent()
				};
			
			data.galleryOptions = options;
			data.gallery = data.gallery || target;
			
			data[orientation] = {current: c, original: $.extend({}, c)};
			
			t.data("jqt-photo-info", data);
		});
	}
	
	/**
	*    Distributive handling for touchstart events
	*
	*    To Do: refractor to use all event based triggers
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function touchStart (event) {
		var target = $(event.target.nodeType == 3 ? event.target.parentNode : event.target), 
			$this = $(this),
			options = $this.data("jqt-photo-options"),
			tt = supportsTouch ? event.targetTouches : {length: !event.ctrlKey ? 1 : 2};
		
		if (target.is(options.tagNames)) {
			return null;
		}
		
		if (target.is(options.playSelector)) {
			if (!$this.hasClass(options.playingClass) && options.play) {
				return options.play.call(this, event);
			}
			
			return true;
			
		} else if (target.is(options.pauseSelector)) {
			if ($this.hasClass(options.playingClass) && options.pause) {
				return options.pause.call(this, event);
			}
			
			return true;
			
		} else if (target.is(options.nextSelector) && options.next) {
			return options.next.call(this, event);
			
		} else if (target.is(options.prevSelector) && options.prev) {
			return options.prev.call(this, event);
			
		} else if (tt.length == 2 && target.closest(options.imageSelector).length && options.scaleStart) {
			if ($this.hasClass(options.playingClass) && options.pause) {
				options.pause.call(this);
			}
			
			return options.scaleStart.call(this, event);
			
		} else if (tt.length == 1 && options.dragStart) {
			if ($this.hasClass(options.playingClass) && options.pause) {
				options.pause.call(this, event);
			}
			
			return options.dragStart.call(this, event);
		}
		
		return true;
	}
		
	/**
	*    Initialize a drag event
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function dragStart (event) {
		event.preventDefault();
		window.scrollTo(0,1);
		
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			table = $this.find(options.tableSelector).css("webkitTransitionDuration", "0s"),
			position = table.data("jqt-photo-position"),
			target = $(event.target).closest(options.imageSelector),
			data = target.data("jqt-photo-info") || false,
			offset = !!data && data[orientation] || false,
			current = !!offset && offset.current || {},
			tt = supportsTouch ? event.targetTouches[0] : event;
		
		this.addEventListener(events.move, options.drag, false);
		this.addEventListener(events.end, options.dragEnd, false);
		
		$this
			.data("jqt-photo-event", {
				table: table,
				position: {current: position.x, original: position.x},
				target: target.css("webkitTransitionDuration", "0s"),
				options: options,
				slides: table.find(options.slideSelector),
				moved: false,
				x: tt.pageX,
				y: tt.pageY,
				left: current.left || 0,
				top: current.top || 0,
				timeStamp: +new Date
			});
			
		return true;
	}
	
	/**
	*    Perform a drag event
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function drag (event) {
		event.preventDefault();
		
		var $this = $(this),
			original = $this.data("jqt-photo-event"),
			table = original.table,
			position = original.position,
			target = original.target,
			info = target.data("jqt-photo-info"),
			data = !!info && info[orientation] || false,
			current = data.current || false,
			tt = supportsTouch ? event.targetTouches[0] : event,
			distanceX = original.x - tt.pageX,
			distanceY = original.y - tt.pageY,
			w, h, x, y, l, t, d, 
			s = current.scale;
		
		original.moved = true;
		
		//image
		if (data) {
			$this.triggerHandler("jqt-photo-hide-toolbars", [original.options]);
			
			if (distanceX) {
				x = target.width() * s;
				w = current.parent.width();
				l = original.left;
				
				if (x > w) {
					l -= distanceX / s;
					distanceX = 0;
					d = ((x - w) / s) / 2;
					
					if (l > d) {
						distanceX = d - l;
						l = d;
					} else if (l < -d) {
						distanceX = -(l + d);
						l = -d;
					}
				}
				
				current.left = floor(l);
			}
			
			if (distanceY) {
				y = target.height() * s;
				h = current.parent.height(),
				t = original.top;
				
				if (y > h) {
					t -= distanceY / s;
				}
				
				current.top = floor(t);
			}
			
			target.css({
				webkitTransitionDuration: "0s",
				webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
			});
		}
		
		//table
		position.current = position.original - distanceX;
		table.css({
				webkitTransitionDuration: "0s",
				webkitTransform: format(original.options.transform, position.current)
		});
		
		return true;
	}
	
	/**
	*    End and clean up a drag event
	*
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function dragEnd (event) {
		var $this = $(this),
			original = $this.data("jqt-photo-event"),
			table = original.table,
			tablePosition = table.data("jqt-photo-position"),
			position = original.position,
			target = original.target,
			info = target.data("jqt-photo-info"),
			data = !!info && info[orientation] || false,
			current = data.current || false,
			slides = original.slides,
			h, x, y, t, d, n, w,
			s = current.scale;
		
		this.removeEventListener(events.move, original.options.drag, false);
		this.removeEventListener(events.end, original.options.dragEnd, false);
		
		if (!original.moved) {
			$this.trigger("jqt-photo-toggle-toolbars");
			return dispatchClick(event.target);
		}
		
		event.preventDefault();
		
		//image
		if (data) {
			y = target.height() * s;
			h = current.parent.height(),
			t = current.top;
			
			if (y > h) {
				d = ((y - h) / s) / 2;
				
				if (t > d) {
					t = d;
				} else if (t < -d) {
					t = -d;
				}
			}
			
			current.top = floor(t);
			
			target.css({
				webkitTransitionDuration: original.options.scaleSpeed + "ms",
				webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
			});
		}
		
		d = (+new Date() - original.timeStamp);
		
		//table
		if (position.current > 0) {
			tablePosition.x = 0;
			
		} else if (position.current < (slides.eq(slides.length-1).width() - table.outerWidth())) {
			tablePosition.x = (slides.eq(slides.length-1).width() - table.outerWidth());
			
		} else {
			n = tablePosition.x < position.current ? -1 : 1;
			x = slides.filter("."+original.options.currentClass);
			t = abs(x.attr("offsetLeft") + position.current);
			w = x.width() / 3;
			
			if (t > w) {
				$this.triggerHandler("jqt-photo-slideto", [slides.index(x[0]) + n, (d > 1000 ? 1000 : d), original.options, slides]);
				return true;
			}
		}
		
		table.css({
			webkitTransitionDuration: (d > 1000 ? 1000 : d) + "ms",
			webkitTransform: format(original.options.transform, tablePosition.x)
		});
		
		return true;
	}
	
	/**
	*    Initialize a scaling event
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function scaleStart (event) {
		event.preventDefault();
		window.scrollTo(0,1);
		
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			tt = supportsTouch ? 
					event.targetTouches : 
					[
						{pageX: controlPoints.x - event.pageX, pageY: controlPoints.y - event.pageY},
						event
					];
		
		this.addEventListener(events.move, options.scale, false);
		this.addEventListener(events.end, options.scaleEnd, false);
		
		try{
			this.removeEventListener(events.move, options.drag, false);
			this.removeEventListener(events.end, options.dragEnd, false);
		}catch(e){}
		
		$this.data("jqt-photo-event", {
				target: $(event.target).closest(options.imageSelector).css("webkitTransitionDuration", "0s"),
				options: options,
				distance: sqrt(
					pow((tt[1].pageX - tt[0].pageX), 2)
					+ pow((tt[1].pageY - tt[0].pageY), 2)
				)
			})
			.find(".image-list")
			.css("webkitTransitionDuration", "0s");//end any transform on this table
			
		return true;
	}
	
	/**
	*    Perform a scaling event
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function scale (event) {
		if (event.targetTouches.length != 2) {
			return true;
		}
		
		event.preventDefault();
		
		var $this = $(this),
			original = $this.data("jqt-photo-event"),
			target = original.target,
			info = target.data("jqt-photo-info"),
			data = !!info && info[orientation] || false,
			tt = event.targetTouches,
			distance = sqrt(
				pow((tt[1].pageX - tt[0].pageX), 2) +
				pow((tt[1].pageY - tt[0].pageY), 2)
			),
			difference = distance - original.distance,
			percentChange = (difference / original.distance) / 2,
			current = data.current,
			transform;
		
		if (!current) {
			parseImageData($this, target, original.options);
			info = target.data("jqt-photo-info");
			data = !!info && info[orientation] || false;
			current = data.current;
		}
		
		$this.triggerHandler("jqt-photo-hide-toolbars", [original.options]);
		
		transform = format(original.options.imageTransform, 
						(current.scale = (current.scale + (current.scale * percentChange))), 
						current.left, 
						current.top                        
				);
		
		original.distance = distance;
			
		target.css({webkitTransitionDuration: "0s", webkitTransform: transform});
		
		return true;
	}
	
	/**
	*    End a scaling event
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function scaleEnd (event) {
		event.preventDefault();
		
		var $this = $(this),
			original = $this.data("jqt-photo-event"),
			info = original.target.data("jqt-photo-info"),
			data = !!info && info[orientation] || false,
			current = data.current;
		
		this.removeEventListener(events.move, original.options.scale, false);
		this.removeEventListener(events.end, original.options.scaleEnd, false);
		
		if (!current) {
			parseImageData($this, original.target, original.options);
			info = original.target.data("jqt-photo-info");
			data = !!info && info[orientation] || false;
			current = data.current;
		}
		
		if (current.scale < data.original.scale) {
			current.scale = data.original.scale;
			current.left = data.original.left;
			current.top = data.original.top;
		}
		
		original.target.css({
			webkitTransitionDuration: original.options.scaleSpeed + "ms",
			webkitTransform: format(original.options.imageTransform, current.scale, current.left, current.top)
		});
		
		return true;
	}
	
	/**
	*    Dispatch a click event on a given target, in the event 
	*    a single-target touch event did not result in movement
	*
	*    @param Element target
	*    @return Boolean
	*/
	function dispatchClick (target) {
		var theEvent = target.ownerDocument.createEvent("MouseEvents");
		theEvent.initEvent("click", true, true);
		target.dispatchEvent(theEvent);
		return true;
	}
	
	/**
	*    Begin a slideshow
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Number index
	*    @return Boolean
	*/
	function play (event, index) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			slides = $this.find(options.slideSelector),
			n = index === undefined || index < 0 
				? Number(slides.filter("."+options.currentClass).attr(options.dataAttribute))
				: abs(index);
				
		addHover($this.addClass(options.playingClass), $this.find(options.pauseSelector)[0]);
		
		if (n != index) {
			$this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
		}
		
		$this.data("jqt-photo-slide-timer", setInterval(function(){slideInterval($this, slides, options)}, options.slideDelay));
			//.triggerHandler("jqt-photo-hide-toolbars", [options]);
		
		return true;
	}
	
	/**
	*    Handler for slideshow intervals
	*
	*
	*
	*    @param jQuery target
	*    @param jQuery slides
	*    @param Object options
	*    @return null
	*/
	function slideInterval (target, slides, options) {
		//current index + 1
		var index = Number(target.find(options.slideSelector).filter("."+options.currentClass).attr(options.dataAttribute)) + 1,
			func = "jqt-photo-slideto";//"slideTo";
		
		if (index === options.data.length) {
			if (!options.repeatSlideShow) {
				target.triggerHandler("jqt-photo-pause");
				return null;
			}
			index = 0;
			func = "jqt-photo-goto";//"goTo";
		}
		
		//options[func].call(target[0], {}, index, options.scrollSpeed, options);
		target.triggerHandler(func, [index, options.scrollSpeed, options, slides]);
		
		target.triggerHandler("jqt-photo-hide-toolbars", [options]);
		
		return null;
	}
	
	/**
	*    End a slideshow
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function pause (event) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this), 
			timer = $this.data("jqt-photo-slide-timer"), 
			options = $this.data("jqt-photo-options");
			
		addHover($this, $this.find(options.playSelector)[0]);
		
		clearInterval(timer);
		$this.removeClass(options.playingClass)
			.data("jqt-photo-slide-timer", null)
			.triggerHandler("jqt-photoshow-toolbars");
		
		return true;
	}
	
	/**
	*    Perform a slide to a given, or inferred next slide, using the data provided
	*
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Number index
	*    @param Number duration
	*    @param Object options
	*    @param jQuery slides - optional;
	*    @return Boolean
	*/
	function slideTo (event, index, duration, options, slides) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this), table, current, title, last, img;
			
		options = options || $this.data("jqt-photo-options");
		
		slides = slides || $this.find(options.slideSelector);
		
		table = options.table;
		
		index = Number(index || 0);
		//how long should this particular transition take?
		duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
		
		if (index >= options.data.length) {
			$this.triggerHandler("jqt-photo-goto", [0, duration, options, slides]);
			return true;
		}
		
		if (index < 0) {
			index = options.data.length + index;
		}
		
		last = slides.filter("."+options.currentClass);
		
		//new current
		current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
		
		img = current.find(options.imageSelector);
		
		if (img.attr("src") != options.data[index].src) {
			img.parent().addClass(options.notLoadedClass);
			img.attr("src", options.data[index].src);
		}
		
		//new position
		position = -current.addClass(options.currentClass).attr("offsetLeft");
	
		if (position > 0) {
			position = 0;
		} else if (position < -(table.width()-current.width())) {
			position = -(table.width()-current.width());
		}
		
		//trigger slide change event
		$this.trigger("jqt-photo-slide-change", [index, duration, position]);
		//save the position
		table.data("jqt-photo-position").x = position;
		//atach animation end listener, and set necessary css properties
		table.one("webkitTransitionEnd", function() {
			options.rearrange(current, last, options);
		})
		.css({
			webkitTransitionDuration: duration+"ms",
			webkitTransform: format(options.transform, position)
		});
		
		//update the title of the gallery
		title = $this.find(options.titleSelector);
		title.html(format(options.galleryName || title.html(), Number(index) + 1, options.data.length));
		
		return true;
	}
	
	/**
	*    Jump to a given, or inferred next slide, using the data provided
	*
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Number index    - optional; index of the slide to go to
	*    @param Number duration - optional; duration of the transition
	*    @param Object options - optional; the options associated with the gallery
	*    @param jQuery slides - optional;
	*    @return Boolean
	*/
	function goTo (event, index, duration, options, slides) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this), table, current, title, last, img;
		
		options = options || $this.data("jqt-photo-options");
		
		slides = slides || $this.find(options.slideSelector);
		
		table = options.table;
		
		index = Number(index || 0);
		//how long should this particular transition take?
		duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
		
		if (index >= options.data.length) {
			index = 0;
		}
		
		if (index < 0) {
			index = options.data.length + index;
		}
		
		last = slides.filter("."+options.currentClass);
		
		//new current
		current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
		
		img = current.find(options.imageSelector);
		
		if (img.attr("src") != options.data[index].src) {
			img.parent().addClass(options.notLoadedClass);
			img.attr("src", options.data[index].src);
		}
		
		//position of the new current
		position = -current.addClass(options.currentClass).attr("offsetLeft");
		
		if (position > 0) {
			position = 0;
		} else if (position < -(table.width()-current.width())) {
			position = -(table.width()-current.width());
		}
		
		//save the position
		table.data("jqt-photo-position").x = position;
		//reset scales after slide and set css to cause the slide
		table.css({
			webkitTransitionDuration: "0s",
			webkitTransform: format(options.transform, position)
		});
		
		//update the title
		//title element
		title = $this.find(options.titleSelector);
		title.html(format(options.galleryName || title.html(), Number(index) + 1, options.data.length));
		
		options.rearrange(current, last, options);
		
		return true;
	}
	
	/**
	*    Reassign the img tags with src attributes in order to maintain the 
	*    memory consumption
	*
	*    @param jQuery current - the current slide
	*    @param jQuery last - the previous slide
	*    @param Object options - the gallery options
	*    @return Boolean
	*/
	function rearrange (current, last, options) {
		var next = current.next()
			prev = current.prev();
			
		next.length && next.find(options.imageSelector).attr("src", options.data[next.attr(options.dataAttribute)].src);
			
		prev.length && prev.find(options.imageSelector).attr("src", options.data[prev.attr(options.dataAttribute)].src);
		
		current.prevAll().slice(options.maxSlidesBefore)
			.find(options.imageSelector)
			.filter("[src]")
			.each(function() {
				var clone = options.blankImage.clone(true),
					$this = $(this);
				
				$this.parent().addClass(options.notLoadedClass);
				$this.replaceWith(clone);
				
				parseImageData($this, clone, options);
			});
			
		current.nextAll().slice(options.maxSlidesAfter)
			.find(options.imageSelector)
			.filter("[src]")
			.each(function() {
				var clone = options.blankImage.clone(true),
					$this = $(this);
				
				$this.parent().addClass(options.notLoadedClass);
				$this.replaceWith(clone);
				
				parseImageData($this, clone, options);
			});
		
		if (last[0] !== current[0]) {
			resetDimensions(last.find(options.imageSelector));
		}
		
		return true;
	}
	
	/**
	*    Transition to previous slide (or last slide) in gallery
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function prev (event) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			slides = $this.find(options.slideSelector),
			index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
		
		addHover($this, event.target);
		
		$this.triggerHandler("jqt-photo-goto", [index - 1, 0, options, slides]);
		
		return true;
	}
	
	/**
	*    Transition to next slide (or first slide) in gallery
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @return Boolean
	*/
	function next (event) {
		event.preventDefault && event.preventDefault();
		
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			slides = $this.find(options.slideSelector),
			index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
		
		addHover($this, event.target);
		
		$this.triggerHandler("jqt-photo-goto", [index + 1, 0, options, slides]);
		
		return true;
	}
	
	/**
	*    Trigger an animation to slide up/slide down the toobars on a given gallery
	*    using a predetermined class
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Object options
	*    @return null
	*/
	function toggleToolbars (event, options) {
		if (event && event.target && event.target.tagName === "A") {
			return null;
		}
		
		var gallery = $(this);
		
		options = options || gallery.data("jqt-photo-options");
		
		var cap;
		
		if (gallery.hasClass(options.toolbarHideClass)) {
			gallery.triggerHandler("jqt-photo-show-toolbars", [options]);
			cap = 90;
		} else {
			gallery.triggerHandler("jqt-photo-hide-toolbars", [options]);
			cap = 45;
		}
		
		var top;
		if (abs(window.orientation) == 90) {
			top = (270 - cap) + "px";
		} else {
			top = (420 - cap) + "px";
		}
		$('.jqt-photo-caption').css({
			top: top
		});
		
		return null;
	}
	
	/**
	*    Trigger an animation to slide up the toobars on a given gallery
	*    using a predetermined class
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Object options
	*    @return null
	*/
	function hideToolbars (event, options) {
		var gallery = $(this);
		
		options = options || gallery.data("jqt-photo-options");
		
		if (!gallery.hasClass(options.toolbarHideClass)) {
			gallery.one("webkitTransitionEnd",function() {
				gallery.addClass(options.toolbarHideClass)
					.removeClass(options.toolbarAnimationOutClass);
			})
			.addClass(options.toolbarAnimationOutClass);
			
			var cap = 45;
			var top;
			if (abs(window.orientation) == 90) {
				top = (270 - cap) + "px";
			} else {
				top = (420 - cap) + "px";
			}
			$('.jqt-photo-caption').css({
				top: top
			});
		}
		return null;
	}
	
	/**
	*    Trigger an animation to slide down the toobars on a given gallery
	*    using a predetermined class
	*
	*
	*    @context The gallery element
	*
	*    @param Event event
	*    @param Object options
	*    @return null
	*/
	function showToolbars (event, options) {
		var gallery = $(this);
		
		options = options || gallery.data("jqt-photo-options");
		
		if (gallery.hasClass(options.toolbarHideClass)) {
			gallery.one("webkitTransitionEnd",function() {
				gallery.removeClass(options.toolbarAnimationInClass);
			})
			.addClass(options.toolbarAnimationInClass)
			.removeClass(options.toolbarHideClass);
			var cap = 90;
			var top;
			if (abs(window.orientation) == 90) {
				top = (270 - cap) + "px";
			} else {
				top = (420 - cap) + "px";
			}
			$('.jqt-photo-caption').css({
				top: top
			});
		}
		return null;
	}
	
	/**
	*    The jQTouch animation start event
	*
	*    @context The gallery element
	*
	*    @param jQuery.Event event
	*    @param Object info
	*    @return null
	*/
	function pageAnimationStart (event, info) {
		if (info && info.direction == "in") {
			var $this = $(this),
				options = $this.data("jqt-photo-options"),
				slides = $this.find(options.slideSelector),
				index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute)),
				n = index > -1 ? index : (options.defaultIndex > -1 ? options.defaultIndex : 0);
				//n = (options.defaultIndex > -1 ? options.defaultIndex : 0);
					
			if (slides.index("."+options.currentClass) != n) {
				$this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
				$this.triggerHandler("jqt-photo-show-toolbars");
			}
		}
		return null;
	}
	
	/**
	*    The jQTouch animation end event
	*
	*    @context The gallery element
	*
	*    @param jQuery.Event event
	*    @param Object info
	*    @return null
	*/
	function pageAnimationEnd (event, info) {
		var $this = $(this),
			options = $this.data("jqt-photo-options"),
			slides = $this.find(options.slideSelector),
			index,n;
			//n = (options.defaultIndex > -1 ? options.defaultIndex : 0);
				
		if (info && info.direction == "out") {
			$this.triggerHandler("jqt-photo-goto", [options.defaultIndex, 0, options, slides]);
			$this.triggerHandler("jqt-photo-show-toolbars");
		} else {
			index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
			n = index > -1 ? index : (options.defaultIndex > -1 ? options.defaultIndex : 0)
			$this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
		}
		return null;
	}
	
	/**
	*    Create a slide for insertion using the provided information
	*
	*    @param Object data - the information about the slide
	*    @param Number index - index of the data in the list
	*    @param Object options - the information about the gallery
	*    @param Boolean inWindow - in the slide window
	*/
	function createSlide (data, index, options, inWindow) {
		var slide = $(data.template || options.slideTemplate).attr(options.dataAttribute, index),
			img = slide.find(options.imageSelector);
		
		parseImageData(options.gallery, img.load(options.loader), options);
		
		if (inWindow) {
			img.attr("src", data.src);
		}
		
		if (data.caption) {
			slide.find(options.captionSelector).html(data.caption);
		}
						
		return slide;               
	}
	
	/**
	*    Reset the scale and position of the given images
	*
	*    @param jQuery images
	*    @return jQuery
	*/
	function resetDimensions (images) {
		return images.each(function(i) {
			var t = $(this),
				data = t.data("jqt-photo-info"),
				d = data[orientation];//,p;
			
			/*if (!d) {
				p = t.parent(defaults.gallerySelector);
				parseImageData(p, t, p.data("jqt-photo-options"));
				d = data[orientation];
			}*/
			
			//image should be resized?
			if (d && d.current.scale != 1 && d.current.scale != d.original.scale) {
				t.css({
					webkitTransitionDuration: "0s", 
					webkitTransform: format(data.galleryOptions.imageTransform, 
											d.current.scale = d.original.scale,
											d.current.left = d.original.left,
											d.current.top = d.original.top
									)
				});
				
				d.current.width = d.original.width;
				d.current.height = d.original.height;
			}
			return null;
		});
	}
	
	/**
	*    Add the hover class to the given element on touchstart
	*
	*    @param jQuery gallery
	*    @param DOMElement | jQuery element
	*    @return null
	*/
	function addHover (gallery, element) {
		$(element).addClass("hover")
		gallery.one("touchend", function(){$(element).removeClass("hover")});
		return null;
	}
	
	/**
	*    Format a String followed by a set of arguments using the format
	*    {0} is replaced with arguments[1]
	*
	*    @param String s
	*    @param Object arg1 ... argN
	*    @return String
	*/
	function format (s) {
		var args = arguments;
		return s.replace(/\{(\d+)\}/g, function(a,b){return args[Number(b)+1] + ""});
	}
	
	/**
	*    window.onorientationchange event handler updates all slide data for the new orientation
	*    (if it doesn't already exist) and resets the scale of the images
	*
	*    @return null
	*/
	function onorientationchange () {
		orientation = abs(window.orientation) == 90 ? "landscape" : "portrait";
		
		//effectively this shouldn't be necessary, but it appears to be
		$("#jqt").removeClass("portrait landscape").addClass(orientation);
		
		//make sure the slides line up appropriately, use delay to ensure repaint has passed
		setTimeout(function(){
			var playing = [],
				loop = function() {
					var $this = $(this), 
						options = $this.data("jqt-photo-options"),
						slides = $this.find(options.slideSelector),
						images =  slides.find(options.imageSelector);
					
					//if ($this.is(":visible")) {
						alignSlides($this, slides, options);
					//}
					
					if ($this.hasClass(options.playingClass)) {
						playing.push(this);
						$this.triggerHandler("jqt-photo-pause");
					}
					
					//if (!images.data("jqt-photo-info")[orientation]) {
					//    parseImageData($this, images, defaults);
					//}
					
					resetDimensions(images.filter("[src]"));
				};
			
			$(galleries).filter(":visible").each(loop).end().filter(":not(:visible)").each(loop);
			
			$(playing).triggerHandler("jqt-photo-play");
			
		}, 10);
		
		var r_height, r_width;
		if (abs(window.orientation) == 90) {
			r_width = 480
			r_height = 270;
		} else {
			r_width = 320
			r_height = 420;
		}
		
		controlPoints = {x: r_width/2, y: r_height/2};
		
		return null;
	}
	
	/**
	*    Reset the transform after an orientation change
	*    to ensure that the slides are correctly aligned
	*
	*
	*
	*    @param jQuery galleries
	*    @return jQuery
	*/
	function alignSlides ($this, slides, options) {
		window.scrollTo(0,1);
		table = $this.find(options.tableSelector),
		position = -slides.filter("."+options.currentClass).attr("offsetLeft");
				
		table.data("jqt-photo-position").x = position;
		
		table.css({
			webkitTransitionDuration: "0s",
			webkitTransform: format(options.transform, position)
		});
		
		return null;
	}
	
	/**
	*    image onload event handler
	*
	*    @context An IMG tag
	*
	*    @return null
	*/
	function loader () {
		window.scrollTo(0,1);
		var r_height, r_width;
		if (abs(window.orientation) == 90) {
			r_width = 480
			r_height = 270;
		} else {
			r_width = 320
			r_height = 420;
		}
		
		var innerWidth = r_width,
			innerHeight = r_height,
			$this = $(this),
			w = $this.width(),
			h = $this.height(),
			top, left,
			scale = min(min(w, innerWidth)/w, min(h, innerHeight)/h),
			width = w * scale,
			height = h * scale,
			data = $this.data("jqt-photo-info"),
			options = data.galleryOptions;
		
		$this.parent().removeClass(options.notLoadedClass);
		
		if (!$this.is(":visible")) {
			data.gallery.one("pageAnimationEnd", function(){parseImageData(data.gallery, $this, data.galleryOptions)});
			
		} else if (width > innerWidth || height > innerHeight) {
			
			left = -floor(((width - innerWidth) / scale) / 2);
			
			top = -floor(((height - innerHeight) / scale) / 2);
			
			$this.css({
				webkitTransitionDuration: "0s",
				webkitTransform: format(options.imageTransform, scale, left, top)
			})
			.addClass(options.presizedClass);
			
			data = data[orientation];
			data.current.scale = data.original.scale = scale;
			data.current.left = data.original.left = left;
			data.current.top = data.original.top = top;
			data.current.width = data.original.width = width * scale;
			data.current.height = data.original.height = height * scale;
		} 
		
		return null;
	}
	
	//load the css on dom ready
	$(function() {
		window.scrollTo(0,1);
		var r_height, r_width;
		if (abs(window.orientation) == 90) {
			r_width = 480
			r_height = 270;
		} else {
			r_width = 320
			r_height = 420;
		}
		controlPoints = {x: r_width/2, y: r_height/2};
		
		if (defaults.useDynamicStyleSheet) {
			var stringRules = "", 
				rules = cssRules, 
				o = r_width > r_height ? "portrait" : "landscape",
				buildProperties = function(name, value) {
					stringRules += name + ":" + ($.isFunction(value) ? value(rules.variables) : value) + ";";
				},
				buildRules = function(name, properties) {
					stringRules += name + "{";
					
					$.each(properties, buildProperties);
					
					stringRules += "}";
				};
			
			$.each(rules.defaults, buildRules);
			$.each(rules[o], buildRules);
			
			
			$(document.createElement("style"))
				.attr({type:"text/css",media:"screen"})
				.html(stringRules)
				.appendTo("head");
			
			//make sure the css is correct on orientationchange
			$(window).one("orientationchange", function() {
				//ensure repaint
				//setTimeout(function() {
					window.scrollTo(0,1);
					
					orientation = abs(window.orientation) == 90 ? "landscape" : "portrait";
		
					//effectively this shouldn't be necessary, but it appears to be
					$("#jqt").removeClass("portrait landscape").addClass(orientation);
					
					stringRules = "";
					
					$.each(rules[orientation], buildRules);
					
					$(document.createElement("style"))
						.attr({type:"text/css",media:"screen"})
						.html(stringRules)
						.appendTo("head");
				//}, 10);
			});
		}
		//Orientation event listener bind
		$(window).bind("orientationchange", onorientationchange);
		//http://cubiq.org/iscroll-3-3-beta-2
		//use resize instead of orientationchange
		//$(window).bind("resize", onorientationchange);
	});
	
})(jQuery);
